#pragma once

#include "Socket.hpp"

#include <memory>
#include <unistd.h>
#include <iostream>

using namespace SocketModule;

class SelectServer
{
    static const int size = sizeof(fd_set) * 8;
    static const int defaultfd = -1;

public:

    SelectServer(int port)
        :_listenSockfd(std::make_unique<TcpSocket>())
        ,_isRunning(false)
    {
        _listenSockfd->buildTcpSocketMethod(port);

        for (int i = 0; i < size; ++i)
        {
            _fd_array[i] = defaultfd;
        }

        _fd_array[0] = _listenSockfd->getFd();
    }

    void start()
    {
        _isRunning = true;
        while (_isRunning)
        {
            // listenSockfd 自己的 IO 也应该放入 select ，让 OS 去通知 listenSockfd 的读事件
            fd_set rfd;
            FD_ZERO(&rfd);
            int maxfd = defaultfd;
            for (int i = 0; i < size; ++i)
            {
                if (_fd_array[i] == defaultfd)
                {
                    continue;
                }

                FD_SET(_fd_array[i], &rfd);

                maxfd = std::max(maxfd, _fd_array[i]);
            }

            printFd();

            int n = select(maxfd + 1, &rfd, nullptr, nullptr, nullptr);

            switch (n)
            {
            case -1:
                LOG(LogLevel::ERROR) << "select error";
                break;
            case 0:
                LOG(LogLevel::INFO) << "time out...";
                break;
            default:
                LOG(LogLevel::DEBUG) << "有事件就绪了... 数量:" << n;
                dispatcher(rfd);        // 事件派发
                break;
            }
        }
    }


    void dispatcher(fd_set& rfd)                    // 事件派发，分为 读新链接 和 读普通事件
    {
        for (int i = 0; i < size; ++i)
        {
            if (_fd_array[i] == defaultfd)
            {
                continue;
            }

            if (FD_ISSET(_fd_array[i], &rfd))
            {

                if (_fd_array[i] == _listenSockfd->getFd())
                {
                    accepter();                     // 处理新链接
                }
                else
                {
                    recver(_fd_array[i], i);       // 普通的读事件
                }
            }
        }
    }

    void accepter()
    {
        InetAddr client;
        int sockfd = _listenSockfd->acceptFd(client);   // 这里 accept 不会阻塞
        if (sockfd < 0)
        {
            return;
        }

        LOG(LogLevel::DEBUG) << "get new link, sockfd:" << sockfd << " client:" << client.getStringAddr();
        
        int pos = 0;
        while (pos < size)                          // 寻找空位
        {
            if (_fd_array[pos] == defaultfd)
            {
                break;
            }
            ++pos;
        }
        if (pos == size)                            // 满了直接关闭
        {
            LOG(LogLevel::WARNING) << "select server full";
            ::close(sockfd);
        }
        else
        {
            _fd_array[pos] = sockfd;                // 注册 sockfd
            LOG(LogLevel::DEBUG) << "register fd success";
        }
    }

    void recver(int fd, int pos)
    {
        char buf[1024];
        ssize_t n = recv(fd, buf, sizeof(buf) - 1, 0);          // 不会阻塞
        if (n > 0)
        {
            buf[n] = 0;
            std::cout << "client say#" << buf << std::endl;     // 打印字符串信息
        }
        else if (n == 0)
        {
            LOG(LogLevel::DEBUG) << "client quit...";

            _fd_array[pos] = defaultfd;             // 注销 fd
            ::close(fd);                            // 并关闭
        }
        else
        {
            LOG(LogLevel::ERROR) << "recv failure";

            _fd_array[pos] = defaultfd;             // 注销 fd
            ::close(fd);                            // 并关闭
        }
    }

    void printFd()
    {
        std::cout << "_fd_array[]: ";
        for (int i = 0; i < size; ++i)
        {
            if (_fd_array[i] == defaultfd)
            {
                continue;
            }
            std::cout << _fd_array[i] << " ";
        }
        std::cout << std::endl;
    }

    void stop()
    {
        _isRunning = false;
    }

    ~SelectServer()
    {
        ;
    }

private:

    std::unique_ptr<Socket> _listenSockfd;
    int _fd_array[SelectServer::size];
    bool _isRunning;
};
